package com.ophelisis.common.util;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;

public class FileUtil {
	@SuppressWarnings("unused")
	private static final Logger logger = Logger.getLogger(FileUtil.class);

	public static final String LS = System.getProperty("line.separator"); // The platform-dependent line separator (e.g., "\n" on UNIX, "\r\n" for Windows)
	public static final String FS = System.getProperty("file.separator");	// The platform-dependent file separator (e.g., "/" on UNIX, "\" for Windows)
	public static final String PS = System.getProperty("path.separator");// The platform-dependent path separator (e.g., ":" on UNIX, "," for Windows)
	public static final String TABS = "\t";
	
	//static String rootDir = File.listRoots()[0].toString();
	static String rootDir = System.getProperty("java.io.tmpdir");
	static int rootDirLen = rootDir.length();

	
	public static void main(String[] args) {
		testNormalizeUrlPath();
	}
	
	
	private static void testNormalizeUrlPath() {
		String[] strs = new String[]{".", ".."
				, "//", "//user", "//a///bc"
				, "//a/.", "/a/b/..", "./..", "a"
				, "/test/../test/file"
				, "test/../test/file"
				, ".././test/../test/file"
				, "../d1/./d2/../f1"
				, "a/b/c/../d/../e"
				, "한글은/.///"
				, "특수문자는?"
				, "멍미:\\temp"
				, "C:\\System Volume Information"
				, "", null
		};
		for(String str : strs) {
			System.out.println(str + TABS + normalizeUrlPath(str,'/'));
		}
	}

	// 단순한 File Read
	public static String readTextFile(File file, String encoding) throws IOException {
		InputStream in = null;
		try {
			in = new FileInputStream(file);
			return IOUtils.toString(in, encoding);
		} finally {
			IOUtils.closeQuietly(in);
		}
	}

	// 단순한 File Read
	public static String readTextFile(String filename, String encoding) throws IOException {
		return readTextFile(new File(filename), encoding);
	}

	// 복잡한 Read
	public static String readTextFile_old(File file, int lineNum, String encoding) throws IOException {
		String line = "";
		try {
		    	//전체 라인수 획득
		    	String totalMsg = readTextFile(file, encoding);
		    	String totMsg[] = totalMsg.split(LS);
		    	int totalLine = totMsg.length;
		    	
		    	int frLine = (lineNum==0?totalLine:(totalLine - lineNum));
		    	if ( frLine < 0 ) frLine = 0;
		    	//System.out.println("====================== Line Count = "+totMsg.length);
		    	for ( int idx = frLine ; idx < totalLine; idx++ ) {
		    	    if ( idx >= 0 ) {
				line += totMsg[idx] + LS;
			    	//System.out.println("====================== totMsg["+(idx)+"] => "+totMsg[idx]);
		    	    }
		    	}
		    	//System.out.println("====================== Line = "+line);
		    	totMsg = null;
		} finally {
		}
		return line;
	}

	// 신규 test Read
	public static String readTextFile(File file, int lineNum, String encoding) throws IOException {
		String line = "";
		int totalLine = 0;
		
		DataInputStream in = null;
		BufferedReader br = null;
		try {
		    FileInputStream fstream = new FileInputStream(file);
		    in = new DataInputStream(fstream);
		    br = new BufferedReader(new InputStreamReader(in));

		    String strLine;
		    //전체 라인수 획득
		    while ((strLine = br.readLine()) != null)   {
			totalLine++;
		    }

		    int frLine = (lineNum==0?totalLine:(totalLine - lineNum));
		    if ( frLine < 0 ) frLine = 0;
		    int idx = 0;
		    
		    br.mark(0);
		    br.reset();
		    while ((strLine = br.readLine()) != null)   {
			if ( idx >= frLine ) {
			    line += strLine + LS;
			    //System.out.println("====================== totMsg["+(idx)+"] => "+totMsg[idx]);
			}
			idx++;
		    }

		    br.close();
		    //Close the input stream
		    in.close();
		} finally {
		    if ( br != null ) br.close();
		    if ( in != null ) in.close();
		}
		return line;
	}	
	public static String readTextFile(String filename, int lineNum, String encoding) throws IOException {
	    
	    if ( lineNum >= 0 )
		return readTextFile(new File(filename), lineNum, encoding);
	    else
		return readTextFile(new File(filename), encoding);
	}

	public static void writeTextFile(File file, String data, String encoding) throws IOException {
		FileUtils.writeStringToFile(file, data, encoding);
	}

	public static void writeTextFile(String filename, String data, String encoding) throws IOException {
		writeTextFile(new File(filename), data, encoding);
	}
	
	public static void close(InputStream is) {
		IOUtils.closeQuietly(is);
	}

	public static void close(OutputStream os) {
		IOUtils.closeQuietly(os);
	}

	public static void skipFully(InputStream in, long len) throws IOException {
		while (len > 0) {
			long ret = in.skip(len);
			if (ret < 0) {
				throw new IOException("Premeture EOF from inputStream");
			}
			len -= ret;
		}
	}
	
	/**
	 * <pre>
	 * Method : FileUtil.copyBytes()
	 * 	InputStream에서 OutputStream으로 정의된 길이만큼 Bytes 복사
	 * <br>
	 * 	return type of void
	 * <br>
	 * @param is
	 * @param os
	 * @param lengthEd
	 * @throws IOException
	 * </pre>
	 */
	public static void copyBytes(InputStream is, OutputStream os, long lengthEd) throws IOException {
		int buffSize = 1024 * 1024; // 1024*4;
		byte buf[] = new byte[buffSize];
		try {
			long totalWrite = 0;
			int bytesRead = is.read(buf);
			while (bytesRead >= 0) {
				if (lengthEd > 0) {
					long byteLeft = lengthEd - totalWrite;
					if (bytesRead >= byteLeft) {
						os.write(buf, 0, (int) (byteLeft));
						// os.flush();
						return;
					}
					totalWrite += bytesRead;
				}
				os.write(buf, 0, bytesRead);
				bytesRead = is.read(buf);
			}
			// os.flush();
		} finally {
			os.close();
		}
	}

	public static void copyBytes(String sourceFile, String targetFile)
			throws IOException {
		File f2 = new File(sourceFile);
		File f3 = new File(targetFile);
		copyBytes(new FileInputStream(f2), new FileOutputStream(f3), f2.length());
	}

	/**
	 * <pre>
	 * Method : FileUtil.copyBytes()
	 * 	path에 있는 sourceFile와 같은 파일을 targetFile로 Bytes 복사
	 * <br>
	 * 	return type of void
	 * <br>
	 * @param path
	 * @param sourceFile
	 * @param String
	 * @param fileNameExtension
	 * @throws IOException
	 * </pre>
	 */
	@SuppressWarnings("rawtypes")
	public static void copyBytes(String path, String sourceFile, String targetFile, String fileNameExtension) throws IOException {
		String fileNumber;
		List pathFileList = FileUtil.getDirFileList(path);
		for (int i = 0; i < pathFileList.size(); i++) {
			if (pathFileList.get(i).toString().indexOf(sourceFile) > 0) {
				fileNumber = pathFileList.get(i).toString().substring((pathFileList.get(i).toString().length() - 14), (pathFileList.get(i).toString().length() - 4));
				FileUtil.copyBytes(pathFileList.get(i).toString(), targetFile + fileNumber + fileNameExtension);
			}
		}
	}
	
	// need more test
	public static String normalizePath(String filename) {
		return FilenameUtils.normalize(filename);
	}


	public static String normalizeUrlPath(String path) {
		return normalizeUrlPath(path, File.separatorChar);
	}

	public static String normalizeUrlPath(String path, char pathChar) {
		String ret = null;
		try {
			String tmpStr = new File(rootDir+path).getCanonicalPath();
			if(tmpStr.length()<=rootDirLen) {
				ret = pathChar+"";
			} else {
				ret = pathChar + tmpStr.substring(rootDirLen, tmpStr.length());
			}
			if(File.separatorChar != pathChar)
				ret = ret.replace(File.separatorChar, pathChar);
		} catch (IOException e) {
			System.out.print(e.getMessage());
			return null;
		}
		return normalize(ret, true);
	}
	
	// http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/util/RequestUtil.java?view=markup
	public static String normalize(String path, boolean replaceBackSlash) {
		if (path == null) return null;

		// Create a place for the normalized path
		String normalized = path;

		if (replaceBackSlash && normalized.indexOf('\\') >= 0)
			normalized = normalized.replace('\\', '/');

		if (normalized.equals("/.")) return "/";

		// Add a leading "/" if necessary
		if (!normalized.startsWith("/"))
			normalized = "/" + normalized;

		// Resolve occurrences of "//" in the normalized path
		while (true) {
			int index = normalized.indexOf("//");
			if (index < 0)
				break;
			normalized = normalized.substring(0, index)
					+ normalized.substring(index + 1);
		}

		// Resolve occurrences of "/./" in the normalized path
		while (true) {
			int index = normalized.indexOf("/./");
			if (index < 0)
				break;
			normalized = normalized.substring(0, index)
					+ normalized.substring(index + 2);
		}

		// Resolve occurrences of "/../" in the normalized path
		while (true) {
			int index = normalized.indexOf("/../");
			if (index < 0)
				break;
			if (index == 0)
				return (null); // Trying to go outside our context
			int index2 = normalized.lastIndexOf('/', index - 1);
			normalized = normalized.substring(0, index2)
					+ normalized.substring(index + 3);
		}

		// Return the normalized path that we have completed
		return (normalized);
	}
	
	/**
	 * 첨부파일 삭제 이벤트
	 * 
	 * @param savePath
	 * @param fileName
	 */
	public static void pathFileDelete(String savePath, String fileName) {
		File fileObj = new File(savePath + "/" + fileName);
		if (fileObj.isFile()) {
			fileObj.delete();
		}
	}

	/**
	 * 첨부파일 다운로드
	 * 
	 * @param request
	 * @param response
	 * @param oriFileName
	 *            - 표시될 파일 이름
	 * @param newFileName
	 *            - 실제 파일 이름
	 * @return
	 * @throws Exception
	 */
	public static void fileDownload(HttpServletRequest request,
			HttpServletResponse response, String savePath, String oriFileName,
			String newFileName) throws Exception {
		File fileObj = new File(savePath + "/" + newFileName);

		try {
			String agentType = request.getHeader("Accept-Encoding");
			try {
				if (!fileObj.exists() || !fileObj.canRead()) {
//					PrintWriter out = response.getWriter();
//					out.println("<script>alert('File Not Found');</script>");
//					out.flush();
//					out.close();
				}
			} catch (Exception e) {
//				PrintWriter out = response.getWriter();
//				out.println("<script>alert('File Not Found');</script>");
//				out.flush();
//				out.close();
			}
			boolean flag = false;
			if (agentType != null && agentType.indexOf("gzip") >= 0)
				flag = true;
			flag = false;
			if (flag) {
				response.setHeader("Content-Encoding", "gzip");
				response.setHeader("Content-disposition", "attachment;filename=" + oriFileName);
				javax.servlet.ServletOutputStream servletoutputstream = response.getOutputStream();
				java.util.zip.GZIPOutputStream gzipoutputstream = new java.util.zip.GZIPOutputStream(servletoutputstream);
				dumpFile(fileObj, gzipoutputstream);
				gzipoutputstream.flush();
				gzipoutputstream.close();
				servletoutputstream.flush();
				servletoutputstream.close();
			} else {
				response.setContentType("application/octet-stream");
				response.setHeader("Content-disposition", "attachment;filename=" + oriFileName);
				javax.servlet.ServletOutputStream servletoutputstream1 = response.getOutputStream();
				dumpFile(fileObj, servletoutputstream1);
				servletoutputstream1.flush();
				servletoutputstream1.close();
			}
		} catch (Exception e) {
//			PrintWriter out = response.getWriter();
//			out.println("<script>alert('File Not Found');</script>");
//			out.flush();
//			out.close();
		}
	}

	private static void dumpFile(File realFile, OutputStream outputstream) {
		byte readByte[] = new byte[4096];
		try {
			BufferedInputStream bufferedinputstream = new BufferedInputStream(
					new FileInputStream(realFile));
			int i;
			while ((i = bufferedinputstream.read(readByte, 0, 4096)) != -1) {
				outputstream.write(readByte, 0, i);
			}
			bufferedinputstream.close();
		} catch (Exception _ex) {
		}
	}

	/**
	 * 파일 존재여부를 확인하는 메소드
	 * 
	 * @param isLivefile
	 * @return
	 */
	public static Boolean fileIsLive(String isLivefile) {
		File f1 = new File(isLivefile);

		if (f1.exists()) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 파일을 생성하는 메소드
	 * 
	 * fileMake("C:/Users/INTERPARK/Desktop/test.txt")
	 * 
	 * @param makeFileName
	 */
	public static void fileMake(String makeFileName) {
		File f1 = new File(makeFileName);
		try {
			f1.createNewFile();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 파일을 삭제하는 메소드
	 * 
	 * fileDelete(inFolder+"\\"+fileName)
	 * 
	 * @param deleteFileName
	 */
	public static void fileDelete(String deleteFileName) {
		File f1 = new File(deleteFileName);
		f1.delete();
	}

	/**
	 * 파일을 복사하는 메소드
	 * 
	 * fileCopy(inFolder+"\\"+fileName, outFolder+"\\"+fileName)
	 * 
	 * @param inFileName
	 * @param outFileName
	 */
	public static void fileCopy(String inFileName, String outFileName) {
		try {
			FileInputStream fis = new FileInputStream(inFileName);
			FileOutputStream fos = new FileOutputStream(outFileName);

			int data = 0;
			while ((data = fis.read()) != -1) {
				fos.write(data);
			}
			fis.close();
			fos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 파일을 이동하는 메소드
	 * 
	 * fileMove(inFolder+"\\"+fileName, outFolder+"\\"+fileName)
	 * 
	 * @param inFileName
	 * @param outFileName
	 */
	public static void fileMove(String inFileName, String outFileName) {
		try {
			FileInputStream fis = new FileInputStream(inFileName);
			FileOutputStream fos = new FileOutputStream(outFileName);

			int data = 0;
			while ((data = fis.read()) != -1) {
				fos.write(data);
			}
			fis.close();
			fos.close();

			// 복사한뒤 원본파일을 삭제함
			fileDelete(inFileName);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 디렉토리의 파일 리스트를 읽는 메소드
	 * 
	 * @param dirPath
	 * @return
	 */
	public static List<File> getDirFileList(String dirPath) {
		// 디렉토리 파일 리스트
		List<File> dirFileList = null;

		// 파일 목록을 요청한 디렉토리를 가지고 파일 객체를 생성함
		File dir = new File(dirPath);

		// 디렉토리가 존재한다면
		if (dir.exists()) {
			// 파일 목록을 구함
			File[] files = dir.listFiles();

			// 파일 배열을 파일 리스트로 변화함
			dirFileList = Arrays.asList(files);
		}
		return dirFileList;
	}
	
	/**
	 * UTF-8 한글깨짐 처리
	 * 
	 * @param str
	 * @return
	 */
	public static String convertString(String str) {
		try {
			return new String(str.getBytes("8859_1"),"UTF-8");
		} catch (UnsupportedEncodingException e) {
			return str;
		}
	}
}
